home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 5
/
Aminet 5 - March 1995.iso
/
Aminet
/
mus
/
edit
/
AlgoRhythms.lha
/
AlgoRhythms
/
Source
/
musicserial.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-12-09
|
16KB
|
612 lines
/* MusicSerial.c
Copyright (c) 1990,1991,1992,1993 by Thomas E. Janzen
All Rights Reserved
THIS SOFTWARE IS FURNISHED FREE OF CHARGE FOR STUDY AND USE AND MAY
BE COPIED ONLY FOR PERSONAL USE OR COMPLETELY AS OFFERED WITH NO
CHANGES FOR FREE DISTRIBUTION. NO TITLE TO AND OWNERSHIP OF THE
SOFTWARE IS HEREBY TRANSFERRED. THOMAS E. JANZEN ASSUMES NO
RESPONSIBILITY FOR THE USE OR RELIABILITY OF THIS SOFTWARE.
Thomas E. Janzen
208A Olde Derby Road
Norwood, MA 02062-1761
(617)769-7733
** FACILITY:
**
** AlgoRhythms music improviser on Commodore (TM) Amiga (TM)
** compiled with SAS/C Amiga Compiler 6.50
**
** ABSTRACT:
**
** MusicSerial.c manages the serial device at the MIDI bit rate.
** All sends to MIDI occur here.
**
** AUTHORS: Thomas E. Janzen
**
** CREATION DATE: 26-MAR-1990
**
** MODIFICATION HISTORY:
** DATE NAME DESCRIPTION
** 7 Dec 90 T. Janzen Used SendIO rather than DoIO - didn't up performance
** 4 Nov 91 T. Janzen incorporate MIDI running status; delete SendNoteOff
** 8 DEC 91 T. Janzen conform to SAS/C 5.10b remove extern from functs
** 4 Jan 92 TEJ last changes for 2.0
** 14 SEP 92 TEJ Make play_note_on use NOTEOFF when dynamic is zero
** (works, fixes stuck note bug)
** 15 SEP 92 TEJ Fix stuck notes by using MAXVOICE in stop_all_notes
* 2 AUG 93 TEJ stop guru's by checking before set audio call
** 6 DEC 94 TEJ remove SERIALNAME from CreatePort. Change flags in
** OpenDevice serial ExtIO (esp. remove 7 wire).
** 10 DEC 94 TEJ Improved error reporting with error number.
** Made it work with audio even though serial device fails.
**--
*/
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/ports.h>
#include <exec/libraries.h>
#include <exec/devices.h>
#include <devices/serial.h>
#include <exec/io.h>
#include <intuition/intuition.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/exec.h>
#include <proto/mathffp.h>
#include <proto/intuition.h>
#ifdef CLI
#include <stdio.h>
#endif
#include "Window.h"
#include "AlgoRhythms.h"
#include "MusicSerial.h"
#include "Record.h"
#include "audio.h"
#define NOTEONCMD (0x90) /* MIDI Note on byte */
#define NOTEOFFCMD (0x80) /* MIDI Note off byte */
#define START (0xFA) /* MIDI Start play byte */
#define STOP (0xFC) /* MIDI Stop play byte */
#define CONTINUE (0xFB) /* MIDI Continue play byte */
#define TIMINGCLOCK (0xF8) /* MIDI Timing clock byte */
static int response;
static struct IOExtSer *ior_ser = NULL;
static struct MsgPort *port = NULL;
static int serial_fubar = FALSE;
extern struct IORequest *CreateExtIO();
extern void DeletePort(struct MsgPort *);
/* strings for error alerts */
static char quit_str[5] = "Quit",
ser_err_str[20] = "Serial Device Error",
prt_err_str[11] = "Port Error",
io_err_str[19] = "Create ExtIO Error",
dev_err_str[20] = "Serial Device Error",
prm_err_str[15] = "Set Parm Error",
wrt_err_str[26] = "Serial.device write error";
#ifndef CLI
struct IntuiText quit_txt
= {2, 1, JAM1, 5, 4, &font_choice, quit_str, NULL};
#endif
static char playbuffer[4]; /* MIDI note message buffer */
#ifdef MEASURE
unsigned int gi_notes_measure = 0;
#endif
static char DBGstr[80];
#ifndef CLI
static struct IntuiText
DBGTxt = {2, 1, JAM1, 5, 15, &font_choice, DBGstr, NULL};
#endif
static void AutoError(const int ErrorNum, const char *ErrorStr);
static int set_serial(struct IOExtSer *io, unsigned long rbuf_len,
unsigned char rlen, unsigned char wlen, unsigned long brk,
unsigned long baud, unsigned char sf,
unsigned long ta0, unsigned long ta1);
static int set_serial(struct IOExtSer *io, unsigned long rbuf_len,
unsigned char rlen, unsigned char wlen, unsigned long brk,
unsigned long baud, unsigned char sf,
unsigned long ta0, unsigned long ta1)
/*
** FUNCTIONAL DESCRIPTION:
** Sets up serial port
**
** RETURN VALUE:
** description:
** data_type:
**
** ARGUMENTS:
**
** io-
** description: IO device structure
** data_type: pointer to struct IOExtSer
** access: read/write
**
** rbuf_len-
** description: read buffer length returned
** data_type: unsigned long
** access: read only
**
** rlen-
** description: length of read buffer
** data_type: unsigned char
** access: read only
**
** wlen-
** description: length of write buffer
** data_type: unsigned char
** access: read only
**
** brk-
** description: length of break time
** data_type: unsigned long
** access: read only
**
** baud-
** description: baud rate
** data_type: unsigned long
** access: read only
**
** sf-
** description: serial flags
** data_type: unsigned char
** access: read only
**
** ta0-
** description: misc flags
** data_type: unsigned long
** access: read only
**
** ta1-
** description: misc flags
** data_type: unsigned long
** access: read only
**
** DESIGN:
** ROUTINE
** : Fill out io structure for setting up serial device
** : send the command to the serial device
** : IF set up failed
** : : set quit and fubar
** : : post error
** : ENDIF
** : return error
** ENDROUTINE
*/
{
auto int error;
io->io_ReadLen = rlen;
io->io_BrkTime = brk; /*length of break time (irrelevant)*/
io->io_Baud = baud;
io->io_WriteLen = wlen;
io->io_StopBits = 0x01;
io->io_RBufLen = rbuf_len;
io->io_SerFlags
|= SERF_XDISABLED | SERF_SHARED | SERF_RAD_BOOGIE;
io->IOSer.io_Command = SDCMD_SETPARAMS;
io->io_TermArray.TermArray0 = ta0;
io->io_TermArray.TermArray1 = ta1;
if (!serial_fubar)
{
if ((error = DoIO(io)) != 0)
{
serial_fubar = TRUE;
AutoError(io->IOSer.io_Error, ser_err_str);
}
}
return error;
}
void open_midi_port(void)
/*
** FUNCTIONAL DESCRIPTION:
** Opens the serial devices for MIDI usage
**
** DESIGN:
** ROUTINE
** : CreatePort to serial device
** : IF failed
** : : set quit and fubar
** : : post error
** : ENDIF
** : ior_ser = CreateExtIO()
** : IF failed
** : : set quit and fubar
** : : post error
** : ENDIF
** : OpenDevice()
** : IF failed
** : : set quit and fubar
** : : post error
** : ENDIF
** : IF not failed yet
** : : set_serial()
** : : IF failed
** : : : set fubar and quit
** : : : post error
** : : ENDIF
** : ENDIF
** : return
** ENDROUTINE
*/
{
auto int error;
auto unsigned long rbl = 512, /*read buffer length*/
brk = 750000, /* length of break in usec */
baud = 31250, /* MIDI baud rate 31.25k bits/sec*/
t0 = 0x51040303, /*termination characters*/
t1 = 0x03030303;
auto unsigned char rwl = 0x08, /*bits per read char */
wwl = 0x08, /*bits per write char */
sf = 0x00; /*serial flags */
port = CreatePort(0, 0);
if (NULL == port)
{
serial_fubar = TRUE;
AutoError(0, prt_err_str);
stop_midi();
}
ior_ser
= (struct IOExtSer *)CreateExtIO(port, sizeof(struct IOExtSer));
if (NULL == ior_ser)
{
serial_fubar = TRUE;
AutoError(ior_ser->IOSer.io_Error, io_err_str);
stop_midi();
}
ior_ser->io_SerFlags
= SERF_XDISABLED | SERF_SHARED | SERF_RAD_BOOGIE;
if ((error = OpenDevice(SERIALNAME, 0,
(struct IORequest *)ior_ser, 0)) != 0)
{
serial_fubar = TRUE;
AutoError(ior_ser->IOSer.io_Error, dev_err_str);
stop_midi();
}
if (!gi_quit && !gi_fubar && !serial_fubar)
{
if ((error
= set_serial(ior_ser, rbl, rwl, wwl, brk, baud, sf, t0, t1)) != 0)
{
serial_fubar = TRUE;
AutoError(ior_ser->IOSer.io_Error, prm_err_str);
stop_midi();
}
}
return;
/* The serial device is open, so go ahead and play music.*/
}
int write_ser(struct IOExtSer *ser_io, char *data, int length)
/*
** FUNCTIONAL DESCRIPTION:
** Write data to serial port.
**
** RETURN VALUE:
** description: TRUE if failed, FALSE if succeeded
** data_type: int
**
** ARGUMENTS:
**
** ser_io-
** description: serial device structure
** data_type: pointer to IOExtSer
** access: read only
**
** data-
** description: data to send to serial device
** data_type: pointer to char
** access: read only
**
** length-
** description: length in bytes of serial data
** data_type: int
** access: read only
**
** DESIGN:
** ROUTINE
** : WaitIO() in case some send is not done yet
** : copy send data and length into ser_io
** : DoIO(ser_io) (send data)
** : IF failed
** : : set quit and fubar
** : : post error
** : ENDIF
** : return error
** ENDROUTINE
*/
{
auto int error;
WaitIO(ser_io);
ser_io->IOSer.io_Data = (APTR)data;
ser_io->IOSer.io_Length = length;
ser_io->IOSer.io_Command = CMD_WRITE;
error = DoIO(ser_io);
if (error)
{
serial_fubar = TRUE;
AutoError(ser_io->IOSer.io_Error, wrt_err_str);
stop_midi();
}
return error;
}
void play_note_on(NOTE_EVENT_TYPE *play_event)
/*
** FUNCTIONAL DESCRIPTION:
** Sends note on and note off MIDI commands; tracks running status
**
** ARGUMENTS:
**
** play_event-
** description: Information about a voice and its status
** data_type: pointer to NOTE_EVENT_TYPE
** access: read only
**
** DESIGN:
** ROUTINE
** : IF channel is negative
** : : clear running status
** : : return
** : ENDIF
** : copy channel and NOTEONCMD to playbuffer[0]
** : copy dynamic to playbuffer[2]
** : IF this is a note-on
** : : playbuffer[1] = new pitch from scale
** : ELSE
** : : playbuffer[1] = old pitch
** : ENDIF
** : IF playbuffer[0] == Running_Status
** : : write_ser &playbuffer[1], length of 2
** : ELSE running status has changed
** : : write_ser &playbuffer[0], length of 3
** : : set new running status
** : ENDIF
** ENDROUTINE
*/
{
static unsigned char Running_Status = 0X00;
if ((play_event->nv_i_channel) < 0)
{
Running_Status = 0;
return;
}
playbuffer[0] = (unsigned char)
(((play_event->nv_i_channel) & 0XF) | NOTEONCMD);
playbuffer[2] = (unsigned char)(play_event->nv_i_dynamic);
playbuffer[1] = (unsigned char)(play_event->nv_i_cur_pitch);
#ifdef MEASURE
if (play_event->nv_i_dynamic != 0)
{
gi_notes_measure++;
}
#endif
if (!serial_fubar) {
if (playbuffer[0] == Running_Status)
{
write_ser(ior_ser, &playbuffer[1], 2); /* send it out MIDI */
}
else
{
write_ser(ior_ser, playbuffer, 3); /* send it out MIDI */
Running_Status = playbuffer[0];
}
}
play_event->nv_i_was_audio = 0;
return;
}
void send_function(const int Function)
/*
** FUNCTIONAL DESCRIPTION:
** Sends special MIDI functions down serial line
**
** ARGUMENTS:
**
** Function-
** description: the MIDI function to send out MIDI
** data_type: int
** access: read only
**
** DESIGN:
** ROUTINE
** : clear playbuffer[1]
** : CASE Function
** : : STARTFUNCT
** : : : set playbuffer[0] to START
** : : STOPFUNCT
** : : : set playbuffer[0] to STOP
** : : CLOCKFUNCT
** : : : set playbuffer[0] to TIMINGCLOCK
** : : CONTFUNCT
** : : : set playbuffer[0] to CONTINUE
** : ENDCASE
** : write_ser playbuffer[0] length of 1
** ENDROUTINE
*/
{
playbuffer[1] = 0;
switch (Function)
{
case STARTFUNCT:
playbuffer[0] = (unsigned char)START;
break;
case STOPFUNCT:
playbuffer[0] = (unsigned char)STOP;
break;
case CLOCKFUNCT:
playbuffer[0] = (unsigned char)TIMINGCLOCK;
break;
case CONTFUNCT:
playbuffer[0] = (unsigned char)CONTINUE;
break;
default:
break;
}
if (!serial_fubar) {
write_ser(ior_ser, playbuffer, 1); /* send it out MIDI */
}
return;
}
void stop_all_notes(NOTE_EVENT_TYPE *notes_to_stop)
/*
** FUNCTIONAL DESCRIPTION:
** Sends note-offs for all MIDI notes in all voices
**
** ARGUMENTS:
**
** notes_to_stop-
** description:
** data_type: pointer to NOTE_EVENT_TYPE
** access: read/write only
**
** DESIGN:
** ROUTINE
** : FOR vox_index = 0 to MAXVOICE
** : : IF notes_to_stop[vox_index] is playing
** : : : send a note off for this voice
** : : : IF recording
** : : : : record a note off for this voice
** : : : ENDIF
** : : : Clear playing bit for this voice
** : : : IF fubar
** : : : : break out of FOR
** : : : ENDIF
** : : ENDIF
** : ENDFOR
** : send_function(STOPFUNCT)
** ENDROUTINE
*/
{
auto int vox_index;
for (vox_index = 0; vox_index < MAXVOICE; vox_index++)
{
if (notes_to_stop[vox_index].nv_i_playing)
{
Delay(1);
notes_to_stop[vox_index].nv_i_dynamic = 0;
if (notes_to_stop[vox_index].nv_i_was_audio)
{
play_audio_note(&(notes_to_stop[vox_index]));
}
else
{
play_note_on(&(notes_to_stop[vox_index]));
}
#ifndef CLI
if (recording)
{
record_note_event(&(notes_to_stop[vox_index]));
}
#endif
notes_to_stop[vox_index].nv_i_playing = FALSE;
if (gi_fubar)
{
break;
}
}
}
send_function(STOPFUNCT);
return;
}
void stop_midi(void)
/*
** FUNCTIONAL DESCRIPTION:
** Disconnects from serial device
**
** DESIGN:
** ROUTINE
** : IF port
** : : DeletePort
** : ENDIF
** : IF ior_ser
** : : CloseDevice
** : ENDIF
** ENDROUTINE
*/
{
if (ior_ser != NULL)
{
CloseDevice(ior_ser);
DeleteExtIO(ior_ser);
ior_ser = NULL;
}
if (port != NULL)
{
DeletePort(port);
port = NULL;
}
serial_fubar = TRUE;
return;
}
static void AutoError(const int ErrorNum, const char *ErrorStr) {
auto char NumStr[20];
switch (ErrorNum) {
case SerErr_DevBusy:
sprintf(NumStr, "DevBusy");
break;
case SerErr_BaudMismatch:
sprintf(NumStr, "BaudMismatch");
break;
case SerErr_BufErr:
sprintf(NumStr, "BufErr");
break;
case SerErr_InvParam:
sprintf(NumStr, "InvParam");
break;
case SerErr_LineErr:
sprintf(NumStr, "LineErr");
break;
case SerErr_ParityErr:
sprintf(NumStr, "Parity");
break;
case SerErr_TimerErr:
sprintf(NumStr, "Timer");
break;
case SerErr_BufOverflow:
sprintf(NumStr, "Overflow");
break;
case SerErr_NoDSR:
sprintf(NumStr, "NoDataSetRdy");
break;
case SerErr_DetectedBreak:
sprintf(NumStr, "break came");
break;
default:
sprintf(NumStr, "%ld", ErrorNum);
break;
}
sprintf(DBGstr, "%s %s", ErrorStr, NumStr);
#ifndef CLI
AutoRequest(w, &DBGTxt, &quit_txt, &quit_txt, 0L, 0L, 300L, 60L);
#else
puts(DBGstr);
#endif
return;
}